/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.handler.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ByteProcessor;

import java.util.List;

/**
 * 一个解码器, 在行尾处拆分接收到的 {@link ByteBuf}.
 * <p>
 * {@code "\n"} and {@code "\r\n"} 均被处理.
 * <p>
 * 字节流应采用 UTF-8 字符编码或 ASCII.
 * 当前实现直接转换 {@code byte} 为 {@code char}, 然后将该 {@code char} 与一些小范围(low range)的 ASCII 字符进行比较, 如 {@code '\n'} 或 {@code '\r' }.
 * UTF-8 is not using low range [0..0x7F] byte values for multibyte codepoint representations
 * therefore fully supported by this implementation.
 * <p>
 * 对于更通用的基于定界符的解码器, 请查看 {@link DelimiterBasedFrameDecoder}.
 */
public class LineBasedFrameDecoder extends ByteToMessageDecoder {

    /**
     * 我们愿意解码的最大帧长度.
     */
    private final int maxLength;
    /**
     * 在超过 maxLength 时, 是否立即抛出异常.
     */
    private final boolean failFast;
    /**
     * 解码后的帧是否应该去掉分隔符
     */
    private final boolean stripDelimiter;

    /**
     * True: 如果我们正在丢弃输入, 因为已经超过 maxLength.
     */
    private boolean discarding;
    /**
     * 丢弃的字节数
     */
    private int discardedBytes;

    /**
     * 上一次扫描的位置
     */
    private int offset;

    /**
     * Creates a new decoder.
     *
     * @param maxLength the maximum length of the decoded frame.
     *                  A {@link TooLongFrameException} is thrown if
     *                  the length of the frame exceeds this value.
     */
    public LineBasedFrameDecoder(final int maxLength) {
        this(maxLength, true, false);
    }

    /**
     * Creates a new decoder.
     *
     * @param maxLength      待解码的帧的最大长度. 如果帧的长度超过该值, 将抛出 {@link TooLongFrameException}
     * @param stripDelimiter 解码后的帧是否应该去掉分隔符
     * @param failFast       <tt>true</tt>: 一旦解码器注意到, 帧的长度将超过 <tt>maxFrameLength</tt>,
     *                       将抛出 {@link TooLongFrameException}, 而不管整个帧是否已经被读取.
     *                       <tt>false</tt>: 在超过 <tt>maxFrameLength</tt> 的整个帧被读取后, 再抛出 {@link TooLongFrameException}
     */
    public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) {
        this.maxLength = maxLength;
        this.failFast = failFast;
        this.stripDelimiter = stripDelimiter;
    }

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    /**
     * Create a frame out of the {@link ByteBuf} and return it.
     *
     * @param ctx    the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param buffer the {@link ByteBuf} from which to read data
     * @return frame           the {@link ByteBuf} which represent the frame or {@code null} if no frame could
     * be created.
     */
    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
        final int eol = findEndOfLine(buffer);
        // 前一解码并未丢弃数据
        if (!discarding) {
            // 找到
            if (eol >= 0) {
                final ByteBuf frame;
                // 不含分隔符的帧长度
                final int length = eol - buffer.readerIndex();
                // 分隔符长度
                final int delimLength = buffer.getByte(eol) == '\r' ? 2 : 1;

                // 过长
                if (length > maxLength) {
                    // 跳过过长消息
                    buffer.readerIndex(eol + delimLength);
                    // 触发异常
                    fail(ctx, length);
                    return null;
                }

                // 解码后的帧去掉分隔符
                if (stripDelimiter) {
                    // 读取帧
                    frame = buffer.readRetainedSlice(length);
                    // 跳过分隔符
                    buffer.skipBytes(delimLength);
                }
                // 解码后的帧不去掉分隔符
                else {
                    // 读取 帧 + 分隔符
                    frame = buffer.readRetainedSlice(length + delimLength);
                }

                return frame;
            }
            // 未找到
            else {
                final int length = buffer.readableBytes();
                // 过长
                if (length > maxLength) {
                    discardedBytes = length;
                    // 丢弃所有数据
                    buffer.readerIndex(buffer.writerIndex());
                    discarding = true;
                    offset = 0;
                    // 立即触发异常
                    if (failFast) {
                        fail(ctx, "over " + discardedBytes);
                    }
                }
                return null;
            }
        }
        // 前一解码丢弃数据
        else {
            // 找到
            if (eol >= 0) {
                final int length = discardedBytes + eol - buffer.readerIndex();
                // 分隔符长度
                final int delimLength = buffer.getByte(eol) == '\r' ? 2 : 1;

                buffer.readerIndex(eol + delimLength);
                discardedBytes = 0;
                discarding = false;
                // 丢弃数据后的下一次解码才触发异常
                if (!failFast) {
                    fail(ctx, length);
                }
            }
            // 未找到
            else {
                // 继续丢弃数据
                discardedBytes += buffer.readableBytes();
                // 丢弃所有数据
                buffer.readerIndex(buffer.writerIndex());
                // 数据已经丢弃, 重置 offset = 0
                offset = 0;
            }
            // 因丢弃数据, 此处解码是无意义的, 因此返回 null
            return null;
        }
    }

    private void fail(final ChannelHandlerContext ctx, int length) {
        fail(ctx, String.valueOf(length));
    }

    private void fail(final ChannelHandlerContext ctx, String length) {
        ctx.fireExceptionCaught(
                new TooLongFrameException(
                        "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
    }

    /**
     * 若找到, 返回行尾对应的索引; 否则, 返回 -1.
     */
    private int findEndOfLine(final ByteBuf buffer) {
        int totalLength = buffer.readableBytes();
        int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);
        // 找到
        if (i >= 0) {
            offset = 0;
            // \r\n 情况
            if (i > 0 && buffer.getByte(i - 1) == '\r') {
                i--;
            }
        }
        // 未找到
        else {
            offset = totalLength;
        }
        return i;
    }

}
